using System;
using System.Collections.Generic;
using System.Text;
using NBusiness.CodeDom;
using NBusiness.CodeDom.Compiler;
using NBusiness.ESharp.Compiler;
using NBusiness.Utilities;
using NBusiness.Templates;
using System.Reflection;

namespace NBusiness.ESharp
{
    [Serializable]
    public class ESharpRoot : EntityRoot
    {
        #region Attach
        protected internal override void Attach()
        {
            foreach (EntityFamily family in Families.Values)
                family.Attach();
        } 
        #endregion

        #region initialize
        protected override void InitializeAssignmentLogic()
        {
            Assignments.Add(new AssignmentLogic(
                ESharpParser.Expressions.FamilyDeclaration,
                EntityElementType.FamilyDeclaration,
                EntityElementType.Any));

            Assignments.Add(new AssignmentLogic(
                ESharpParser.Expressions.Identifier,
                EntityElementType.FamilyName,
                new AssignmentLogicHandler(BuildFamily),
                EntityElementType.FamilyDeclaration));

            Assignments.Add(new AssignmentLogic(
                ESharpParser.Expressions.UsingDeclaration,
                EntityElementType.UsingDeclaration,
                new AssignmentLogicHandler(BuildUsing),
                EntityElementType.Any));
        } 
        #endregion

        #region Build family
        private void BuildFamily(object sender, AssignmentLogicEventArgs e)
        {
            EntityFamily family = null;
            string name = e.Elements.Peek().Value;
            if (Families.ContainsKey(name))
            {
                family = Families[name];
            }
            else
            {
                family = new ESharpFamily();
                family.BuildError += new EventHandler<EntityBuildErrorEventArgs>(family_ParseError);
                family.Name = name;
                family.Parent = this;
                Families.Add(name, family);
            }

            family.Build(e.Elements, EntityElementType.FamilyName);
        }

        private void BuildUsing(object sender, AssignmentLogicEventArgs e)
        {
            string path = e.Elements.Peek().Path;

            ESharpUsing entityUsing = new ESharpUsing();
            entityUsing.Root = this;
            entityUsing.Build(e.Elements, EntityElementType.UsingDeclaration);

            if (!Using.ContainsKey(path))
                Using.Add(path, new List<EntityUsing>());

            Using[path].Add(entityUsing);
        }

        void family_ParseError(object sender, EntityBuildErrorEventArgs e)
        {
            OnBuildError(e.Error);
        }
        #endregion

        #region Get element from child

        protected override EntityElement GetElementFromChild(string path, int line, int col)
        {
            EntityElement e = null;
            foreach (EntityFamily f in Families.Values)
            {
                e = f.GetElementByLocation(path, line, col);
                if (e != null) break;
            }

            return e;
        } 
        #endregion

        #region GetUnitFromChild
        protected override EntityCompileUnit GetUnitFromChild(EntityElement element)
        {
            EntityCompileUnit unit = null;
            foreach (EntityFamily family in Families.Values)
            {
                unit = family.GetUnitByElement(element);
                if (unit != null) break;
            }

            return unit;
        } 
        #endregion

        #region Helpers
        internal static int GetLine(string code, int index)
        {
            int line = 0;
            int i = code.IndexOf("\n");

            while (i > 0 && i < index)
            {
                line++;
                i = code.IndexOf("\n", i + 1);
            }
            return line;
        }
        internal static int GetColumn(string code, int index)
        {
            int col = 0;
            while (index - col > 0 && code[index - col - 1] != '\n')
            {
                col++;
            }
            return col;
        } 
        #endregion
    }
}
